﻿using System;
using System.Collections.Generic;
using UnityEngine;

namespace YummyGame.Framework
{
    public class SkillPrototype
    {
        public SkillBaseInfo BaseInfo { get; private set; }
        private SkillDescriptor descriptor;
        private Dictionary<string, SkillActionPrototype> m_actions;
        private Dictionary<string, float> m_local_variables;
        private ExpressionContext m_context;
        private Dictionary<string, Expression> m_expressions;
        private HashSet<string> m_createPath;

        private float _cd;
        

        public SkillPrototype(SkillDescriptor descriptor)
        {
            this.descriptor = descriptor;
            m_actions = new Dictionary<string, SkillActionPrototype>();
            m_local_variables = new Dictionary<string, float>();
            m_expressions = new Dictionary<string, Expression>();
            m_createPath = new HashSet<string>();
            BaseInfo = new SkillBaseInfo();

            Create(descriptor);
        }

        private void Create(SkillDescriptor desc)
        {
            if (m_createPath.Contains(desc.fullPath)) return;
            m_createPath.Add(descriptor.fullPath);
            if (desc.extends != null) Create(desc.extends);
            CreateBaseInfo(desc);
            CreateVariable(desc);
            CreateExpression(desc);
            CreateEffect(desc);
        }

        private void CreateBaseInfo(SkillDescriptor desc)
        {
            SkillKeyValuePair kv = desc.GetBaseInfo();
            if (kv == null) return;
            foreach (var root in kv.field.pairs)
            {
                string rootName = root.key;
                string typeName = "YummyGame.Framework.BaseInfoBuilder_" + rootName;
                Type type = Type.GetType(typeName);
                if (type == null) continue;
                BaseInfoBuilder build = (BaseInfoBuilder)Activator.CreateInstance(type);
                build.Build(BaseInfo, root.field);
            }
        }

        private void CreateExpression(SkillDescriptor desc)
        {
            SkillKeyValuePair kv = desc.GetExpression();
            if (kv == null) return;
            ExpressionParser parser = new ExpressionParser(m_context);
            foreach (var expression in kv.field.pairs)
            {
                m_expressions.Add(expression.key, parser.Parse(expression.field.AsString()));
            }

        }

        private void CreateEffect(SkillDescriptor desc)
        {
            SkillKeyValuePair kv = desc.GetEffect();
            if (kv == null) return;
            foreach (var root in kv.field.pairs)
            {
                string rootName = root.key;
                var action = new SkillRootPrototype();
                foreach (var child in root.field.pairs)
                {
                    var childName = child.key;
                    string typeName = "YummyGame.Framework.ActionBuilder_" + childName;
                    Type type = Type.GetType(typeName);
                    if (type == null) continue;
                    ActionBuilder build = (ActionBuilder)Activator.CreateInstance(type);
                    action.AddChild(build.Build(child.field));
                }
                m_actions.Add(rootName, action);
            }
        }

        private void CreateVariable(SkillDescriptor desc)
        {
            SkillKeyValuePair kv = desc.GetVariable();
            if (kv == null) return;
            foreach (var variable in kv.field.pairs)
            {
                if (!variable.field.IsNumber()) continue;
                DefineVariable(variable.key,variable.field.AsNumber());
            }
            m_context = new ExpressionContext();
            m_context.LocalVariables = m_local_variables;
        }

        public void DefineVariable(string key,float value)
        {
            if (!HasVariable(key)) m_local_variables[key] = value;
            else throw new Exception("变量被重复定义：" + key);
        }

        public void SetVariable(string key,float value)
        {
            if (!HasVariable(key)) throw new Exception("没有定义这个变量：" + key);
            m_local_variables[key] = value;
        }

        public float GetVariable(string key)
        {
            if (m_local_variables.ContainsKey(key)) return m_local_variables[key];
            return 0;
        }

        public bool HasVariable(string key)
        {
            return m_local_variables.ContainsKey(key);
        }

        public Expression GetExpression(string key)
        {
            Expression res = null;
            if (m_expressions.TryGetValue(key, out res)) return res;
            return null;
        }

        public Skill GetSkill(SkillOwner owner)
        {
            IPool<Skill> pool = SingletonPool<Skill>.Instance.Create(() => Activator.CreateInstance<Skill>());
            Skill skill = pool.Get();
            skill.BaseInfo = BaseInfo;
            skill.Prototype = this;
            skill.descriptor = descriptor;
            skill.m_actions = m_actions;
            skill.Owner = owner;
            
            foreach (var kv in m_local_variables)
            {
                skill.DefineVariable(kv.Key, kv.Value);
            }
            skill.m_context = new ExpressionContext();
            skill.m_context.LocalVariables = skill.m_variables;
            skill.m_context.OwnerVariables = owner.m_variables;
            foreach (var kv in m_expressions)
            {
                skill.m_expressions.Add(kv.Key, (Expression)kv.Value.Clone());
                skill.m_expressions[kv.Key].RebindContext(skill.m_context);
            }
           
            return skill;
        }


        public bool CanUse()
        {
            return _cd <= 0;
        }

        public void SetUse()
        {
            _cd = BaseInfo.CD.Calculate();
        }

    }
}
